package gsolver;

import java.util.HashSet;
import java.util.Set;
import java.util.Collections;
import java.util.Arrays;

import gproblem.GJobData;
import gsolver.GChromosome;

/**
 * Classe représentant un lot de jobs. Non-modifiable.
 */
public class Lot {
    private HashSet<Integer> jobs;
    private Double dateLivraisonMax;
    private Double coutStockageUnitaire;
    private Double coutMinimal;
    private Double tailleOccupee;

    /**
     * Crée un lot contenant l'ensemble de jobs en argument.
     */
    public Lot(Set<Integer> newJobs) {
	this.jobs = new HashSet<Integer>(newJobs);
	dateLivraisonMax = null;
	coutStockageUnitaire = null;
	coutMinimal = null;
	tailleOccupee = null;
    }

    /**
     * Retourne une vue non-modifiable de l'ensemble des jobs du lot.
     */
    public Set<Integer> getJobs() {
	return Collections.unmodifiableSet(jobs);
    }

    /**
     * Retourne une copie de l'ensemble des jobs du lot.
     */
    public Set<Integer> getCopyJobs() {
	return new HashSet<Integer>(jobs);
    }

    /**
     * Retourne un nouveau lot avec le job en argument en moins.
     * Si il n'est pas présent, retourne le même lot.
     */
    public Lot retirerJob(int job) {
	HashSet<Integer> newJobs = new HashSet<Integer>(jobs);
	newJobs.remove(job);

	return new Lot(newJobs);
    }

    public Lot retirerJobs(int[] aRetirer) {
	HashSet<Integer> newJobs = new HashSet<Integer>(jobs);
	newJobs.removeAll(Arrays.asList(aRetirer));

	return new Lot(newJobs);
    }

    /**
     * Retourne un nouveau lot avec le job en argument en plus.
     * Si il est déjà présent, retourne le même lot.
     */
    public Lot ajouterJob(int job) {
	HashSet<Integer> newJobs = new HashSet<Integer>(jobs);
	newJobs.add(job);

	return new Lot(newJobs);
    }

    public Lot ajouterJobs(Integer[] aAjouter) {
	HashSet<Integer> newJobs = new HashSet<Integer>(jobs);
	newJobs.addAll(Arrays.asList(aAjouter));

	return new Lot(newJobs);
    }

    /**
     * Retourne la date de livraison maximale du lot, qui est
     * le minimum des dates de livraison de chaque job du lot.
     */
    public double dateLivraisonMax() {
	if (dateLivraisonMax != null) {
	    return dateLivraisonMax;
	} else {
	    double dateMin = Double.MAX_VALUE;
	    GJobData[] jobData = GChromosome.getProblem().getJobData();
	
	    for (Integer i : jobs) {
		if (jobData[i].getDueDate() < dateMin) {
		    dateMin = jobData[i].getDueDate();
		}
	    }
	    dateLivraisonMax = new Double(dateMin);

	    return dateMin;
	}
    }

    /**
     * Retourne le coût de stockage unitaire du lot entier,
     * qui est la somme des coûts de stockage unitaire de chaque
     * job du lot.
     */
    public double coutStockageUnitaire() {
	if (coutStockageUnitaire != null) {
	    return coutStockageUnitaire;
	} else {
	    GJobData[] jobData = GChromosome.getProblem().getJobData();
	    double cout = 0;
	    
	    for (Integer i : jobs) {
		cout = cout + jobData[i].getHoldingCost();
	    }
	    coutStockageUnitaire = new Double(cout);
	    
	    return cout;
	}
    }

    /**
     * Retourne le coût de stockage minimal du lot.
     */
    public double coutMinimal() {
	if (coutMinimal != null) {
	    return coutMinimal;
	} else {
	    GJobData[] jobData = GChromosome.getProblem().getJobData();
	    double cout = 0;
	    
	    for (Integer i : jobs) {
		cout += (jobData[i].getDueDate() - dateLivraisonMax()) * 
			 jobData[i].getHoldingCost();
	    }
	    coutMinimal = new Double(cout);
	    
	    return cout;
	}
    }

    public double tailleOccupee() {
	if (tailleOccupee != null) {
	    return tailleOccupee;
	} else {
	    double taille = 0;
	    
	    for (Integer job : jobs) {
		taille += GChromosome.getProblem().getJobData()[job].getSize();
	    }
	    tailleOccupee = new Double(taille);

	    return taille;
	}
    }

    public String toString() {
	String resultat = "{";
	int i = 0;
	
	for (Integer job : jobs) {
	    resultat += job;
	    i++;
	    if (i != jobs.size()) {
		resultat += ", ";
	    }
	}

	return resultat + "}";
    }
}